package org.september.pisces.filestore.service.hik;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Random;

@Slf4j
@Service
public class VideoClientImpl {

    @Autowired
    private HikPoolService poolService;
    private final String endStr = "\r\n";
    private final String twoHyphens = "--";
    private final String boundaryStr = "############";
    private final String divide = twoHyphens + boundaryStr;
    private final String contentType = "multipart/form-data";
    private static final String PREFIX = "storage";
    private static final String VIDEO_BEST_NODE_BASE_URI = "/ISAPI/Storage/bestNode";
    private static final String UPLOAD_VIDEO_URI = "/ISAPI/Storage/video/write";
    private static final String VIDEO_URI = "/ISAPI/Storage/videoStream/download?key=";

    private String getBestNode(String serialID) {
        return poolService.getBestNode(VIDEO_BEST_NODE_BASE_URI + "?serialID=" + serialID + "&poolID=" + HikPoolService.videoPoolId
                + "&dataType=", PREFIX, 1, "application/json");
    }

    public String uploadVideo(byte[] data) throws JsonProcessingException {
        String gmtTime = GmtTimeUtil.getGmtTime();
        String serialID = new Random().nextInt(1000000) + "";
        String bestNode = getBestNode(serialID);
        JSONObject node = new JSONObject(bestNode);
        String authorization = poolService.postAuth(PREFIX, contentType, gmtTime, UPLOAD_VIDEO_URI, 1);
        String uri = "http://" + node.getStr("gatewayIP") + ":" + node.getStr("gatewayPort") + UPLOAD_VIDEO_URI;
        PostMethod postMethod = new PostMethod(uri);
        postMethod = buildUploadVideoRequestHeader(postMethod, gmtTime, authorization, contentType);
        String body = buildUploadVideoRequestBody(serialID, node.getStr("token"), data.length);
        byte[] bodyAndPic = ByteArrayUtil.byteMerger(body.getBytes(StandardCharsets.UTF_8), data);
        String tail_str = divide + twoHyphens + endStr + endStr;
        byte[] bytes = ByteArrayUtil.byteMerger(bodyAndPic, tail_str.getBytes(StandardCharsets.UTF_8));
        postMethod.setRequestEntity(new ByteArrayRequestEntity(bytes));
        RequestHandler requestHandler = new RequestHandler();
        String result = requestHandler.sendRequest(postMethod);//{"url":"123_41e1e08ecdb152d54d0458e9b5589ddf?range=20250526T100240000-20250526T100252000&timezone=+08:00&devtype=1&fileformat=1&headlen=0"}
        String url = new JSONObject(result).getStr("url");
        String path = URLUtil.getPath(url);
        return path;
    }



    public InputStream getVideoStream(String key) {
        String gmtTime = GmtTimeUtil.getGmtTime();
        String url = VIDEO_URI + key + "&startTime=0&endTime=0&dataType=1";
        String authorization = poolService.getAuth(PREFIX, "", gmtTime, url, 1);
        GetMethod getMethod = new GetMethod("http://" + poolService.getAddress() + url);
        getMethod.addRequestHeader("Authorization", authorization);
        getMethod.addRequestHeader("Host", poolService.getAddress());
        getMethod.addRequestHeader("Date", gmtTime);
        getMethod.addRequestHeader("Connection", "close");

        RequestHandler requestHandler = new RequestHandler();
        return requestHandler.sendRequestReturnStream(getMethod);

    }

    private PostMethod buildUploadVideoRequestHeader(PostMethod postMethod, String gmtTime, String authorization, String contentType) {
        postMethod.setRequestHeader("Host", poolService.getAddress());
        postMethod.setRequestHeader("Accept-Language", "zh-cn");
        postMethod.setRequestHeader("Authorization", authorization);
        postMethod.setRequestHeader("Date", gmtTime);
        postMethod.setRequestHeader("Content-Type", contentType + ";boundary=" + boundaryStr);
        postMethod.setRequestHeader("Connection", "keep-alive");
        return postMethod;
    }


    public String buildUploadVideoRequestBody(String serialID, String token, int videoLen) {
        StringBuilder body = new StringBuilder(divide + endStr);
        body.append(" Content-Disposition: form-data;");
        body.append(" name=\"serialID\"" + endStr + endStr + serialID + endStr + divide + endStr + "Content-Disposition: form-data;");
        body.append(" name=\"poolID\"" + endStr + endStr + HikPoolService.videoPoolId + endStr + divide + endStr + "Content-Disposition: form-data;");
        body.append(" name=\"sourceDataType\"" + endStr + endStr + 0 + endStr + divide + endStr + "Content-Disposition: form-data;");
        body.append(" name=\"token\"" + endStr + endStr + token + endStr + divide + endStr + "Content-Disposition: form-data;");
        body.append(" name=\"dataLength\"" + endStr + endStr + videoLen + endStr + divide + endStr + "Content-Disposition: form-data;");
        body.append(" name=\"data\"" + endStr + "Content-Type: " + " video/h264" + endStr + endStr);
        return body.toString();
    }


    public String uploadVideoFile(String path) {
        try {
            File file = new File(path);
            if (!file.exists()) {
                log.error("file does not exist");
                return null;
            }
            byte[] bytes = ByteArrayUtil.inputStream2ByteArray(path);
            log.info("Uploading video file {},length={} ", path, bytes.length);
            return uploadVideo(bytes);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void downloadVideo(String key, String localPath) {
        InputStream inputStream = getVideoStream(key);
        FileUtil.writeFromStream(inputStream, localPath);
    }

}
